/*
 * Copyright 2013 Luson Foundation and other contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package org.aprilis.luson;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.core.SimpleAnalyzer;
import org.apache.lucene.analysis.core.StopAnalyzer;
import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.aprilis.sys.Constants;
import org.json.simple.JSONArray;
import org.json.simple.JSONValue;
import org.json.simple.parser.JSONParser;

/**
 * @author rk
 * 
 */
public class LusonIndexer {

	/**
	 * Constructor that accepts index directory path as an argument. Lucene
	 * indexes will be created in this location.
	 * 
	 * @param indexDir
	 * @throws IOException
	 */
	public LusonIndexer(String indexDir) {
		msIndexDir = indexDir;
		moAnalyzer = new WhitespaceAnalyzer(Version.LUCENE_43);
		moIdxWriterConfig = new IndexWriterConfig(Version.LUCENE_43, moAnalyzer);

		moIndexWriter = null;
		moJsonParser = new JSONParser();
	}

	public LusonIndexer(String indexDir, Constants.AnalyzerTypes eAnalyzerType) {
		msIndexDir = indexDir;
		moAnalyzer = null;

		switch (eAnalyzerType) {
			case STANDARD :
				moAnalyzer = new StandardAnalyzer(Version.LUCENE_43);
				break;
			case SIMPLE :
				moAnalyzer = new SimpleAnalyzer(Version.LUCENE_43);
				break;
			case STOP :
				moAnalyzer = new StopAnalyzer(Version.LUCENE_43);
				break;
			case WHITESPACE :
			default :
				moAnalyzer = new WhitespaceAnalyzer(Version.LUCENE_43);
				break;
		}

		moIdxWriterConfig = new IndexWriterConfig(Version.LUCENE_43, moAnalyzer);

		moIndexWriter = null;
		moJsonParser = new JSONParser();
	}

	/**
	 * This function adds records present in jsonDocs to the lucene index under
	 * the collection collectionName.
	 * 
	 * jsonDocs can be a single record or an array of records.
	 * 
	 * @param collectionName
	 * @param jsonDocs
	 * @return
	 */
	public boolean index(String sCollectionName, String jsonDocs) {
		try {
			if (moIndexWriter == null)
				initializeIndex(sCollectionName);

			if (moIndexWriter != null) {
				/*
				 * Parse jsonDocs using JSONParser and fetch the array of
				 * records.
				 */
				String newLineTrimmedJson = jsonDocs.replaceAll(Constants.gsNewlineString, Constants.gsEmptyString);
				String spaceRemovedJson = newLineTrimmedJson.replaceAll(Constants.gsRemoveSpacesExcludingQuotes,
						Constants.gsEmptyString);

				@SuppressWarnings("unchecked")
				List<HashMap<String, Object>> recordsArray = (JSONArray) moJsonParser.parse(spaceRemovedJson);

				if (recordsArray.size() == 0)
					return true;

				return indexDocs(sCollectionName, recordsArray);

			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return false;
	}

	/**
	 * 
	 * @param sCollectionName
	 * @param listDocs
	 * @return
	 */
	public boolean indexDocs(String sCollectionName, List<HashMap<String, Object>> listDocs) {

		try {
			if (moIndexWriter == null)
				initializeIndex(sCollectionName);

			for (short shRecIndex = 0; shRecIndex < listDocs.size(); shRecIndex++) {
				HashMap<String, Object> hMapDoc = listDocs.get(shRecIndex);

				String jsonRecord = JSONValue.toJSONString(hMapDoc);

				Document luceneDoc = new Document();

				for (Map.Entry<String, Object> field : hMapDoc.entrySet()) {
					String fieldName = field.getKey().trim();
					String mergedFieldValue = "";

					if (field.getValue() instanceof List<?>) {

						@SuppressWarnings("unchecked")
						List<Object> fldValueList = (List<Object>) field.getValue();

						for (short shFldValIndex = 0; shFldValIndex < fldValueList.size(); shFldValIndex++)
							mergedFieldValue += fldValueList.get(shFldValIndex) + " ";

					} else {
						try {
							mergedFieldValue = field.getValue().toString();
						} catch (Exception e) {
							e.printStackTrace();
							System.out.println("Field Value cannot be parsed to index into " + sCollectionName);

							return false;
						}
					}

					luceneDoc.add(new TextField(fieldName, mergedFieldValue, Field.Store.NO));
					luceneDoc.add(new StringField("record", jsonRecord, Field.Store.YES));
				}
				moIndexWriter.addDocument(luceneDoc);
			}
		} catch (Exception e) {
			e.printStackTrace();

			return false;
		}

		return true;
	}

	public void initializeIndex(String sCollectionName) throws IOException {
		moIndexDir = FSDirectory.open(new File(msIndexDir + "//" + sCollectionName));

		moIndexWriter = new IndexWriter(moIndexDir, moIdxWriterConfig);
	}

	public void commit() throws IOException {
		if (moIndexWriter != null)
			moIndexWriter.commit();
	}

	public void closeIndex() throws IOException {
		if (moIndexWriter != null) {
			moIndexWriter.close();
			moIndexDir.close();

			moIndexDir = null;
			moIndexWriter = null;
		}
	}

	private IndexWriter moIndexWriter;

	private IndexWriterConfig moIdxWriterConfig;

	private Analyzer moAnalyzer;

	Directory moIndexDir;

	private String msIndexDir;

	private JSONParser moJsonParser;
}
